#include <string.h>
#include "base.h"
#include "csv.h"
#include "astr.h"
#include "log.h"

static inline void __csv_line_arr_astr_deinit(void *str)
{
    astr_t *s = (astr_t *)(str);
    astr_deinit(&s);
}

static inline void __csv_line_arr_deinit(void *line_arr)
{
    if (line_arr == NULL)
        return;
    array_t *arr = (array_t *)line_arr;
    array_deinit(&arr);
}

array_t *csv_parse_str(const char *data)
{
    array_t *root = array_init(__csv_line_arr_deinit);
    if (root == NULL)
        goto fail0;
    astr_t *str = NULL;
    array_t *line = NULL;
    char c = '\0';
    while ((c = (*data)) != '\0')
    {
        if (str == NULL)
        {
            str = astr_init(NULL);
            if (str == NULL)
                goto fail0;
        }
        if (line == NULL)
        {
            line = array_init(__csv_line_arr_astr_deinit);
            if (line == NULL)
                goto fail0;
        }

        if (c == '\n' || c == ',')
        {
            array_add(line, str);
            str = NULL;

            if (c == '\n')
            {
                array_add(root, line);
                line = NULL;
            }
        }
        else
        {
            if ((c == '\r' && *(data + 1) == '\n') == 0)
                astr_add(str, c);
        }
        data++;
    }

fail0:
    return root;
}

array_t *csv_parse_file(const char *file_path)
{
    array_t *ret = NULL;
    if (file_path == NULL)
        goto fail0;
    size_t file_size = fsize(file_path);
    if (file_size == -1)
        goto fail0;
    FILE *txt = fopen(file_path, "r");
    if (txt == NULL)
        goto fail0;

    char *data = CSV_MALLOC(file_size + 1);
    if (data == NULL)
        goto fail1;

    size_t n = fread((void *)data, sizeof(char), file_size, txt);
    if (n != file_size)
        goto fail1;
    if (ferror(txt))
        goto fail1;
    ret = csv_parse_str(data);
fail1:
    if (data == NULL)
        CSV_FREE(data);
    fclose(txt);
fail0:
    return ret;
}

array_t *csv_head(array_t *csv_arr)
{
    return array_get(csv_arr, 0);
}

array_t *csv_line(array_t *csv_arr, size_t index)
{
    return array_get(csv_arr, index);
}

int csv_array_add_line(array_t *csv_arr, char **data, size_t data_len)
{
    if (csv_arr == NULL)
        return -1;
    array_t *line = array_init(__csv_line_arr_astr_deinit);
    int ret = 0;
    for (size_t i = 0; i < data_len; i++)
    {
        ret = array_add(line, astr_init(data[i]));
        if (ret)
            return ret;
    }
    return array_add(csv_arr, line);
}

int csv_file_rewrite(const char *file_path, array_t *csv_arr)
{
    if (file_path == NULL || csv_arr == NULL)
        return -1;
    FILE *fp = fopen(file_path, "w");
    if (fp == NULL)
        return -1;
    for (size_t i = 0; i < csv_arr->len; i++)
    {
        array_t *line = array_get(csv_arr, i);
        if (line == NULL)
            continue;
        for (size_t j = 0; j < line->len; j++)
        {
            astr_t *str = array_get(line, j);
            if (str->len > 0)
                fwrite(str->data, str->len, 1, fp);
            fwrite(",", 1, 1, fp);
        }
        fwrite("\r\n", 2, 1, fp);
    }

    fclose(fp);
    return 0;
}

void csv_free(array_t **csv_arr)
{
    array_deinit(csv_arr);
}